# -*- Makefile -*-
#
# $Id$
#
# makefile to configure the C++ Standard library
#
##############################################################################
#
# Creates a C++ header file, config.h, containing a list of (possibly
# commented out) preprocessor #defines in the form _RWSTD_NO_XXX,
# where the XXX is the file name part (w/o the extension) of XXX.cpp
# (some C or C++ program)
#
# Files named NO_XXX.cpp are assumed to be negative tests, which are
# considered successful unless they exit with zero status, in which
# case they are considered failed and the corresponding macro,
# _RWSTD_NO_XXX, is #defined.
#
# Each test named  XXX.lib.cpp is translated into a  static or dynamic
# library (depending  on the presence of shared  in BUILDMODE variable),
# and may  be linked into other  executables. To link a  library into an
# executable  the  name of  the  library  must  be mentioned  in  LDOPTS
# somewhere in the executable's source.
#
# XXX.cpp can but doesn't need to contain main(). If it doesn't, the
# macro _RWSTD_NO_XXX will be commented out in config.h iff XXX.cpp
# successfully compiles.
#
# If XXX.cpp contains main(), _RWSTD_NO_XXX will be commented out iff
# XXX.cpp not only successfully compiles, but also links and runs.
#
# Any output produced by the executable XXX obtained by compiling and
# linking XXX.cpp is appended to the end of config.h.
#
##############################################################################

include ../makefile.in

SRCDIR       = $(TOPDIR)/etc/config/src
VPATH        = $(SRCDIR)

CPPFLAGS    += -I.
CPPFLAGS    := $(filter-out -I$(TOPDIR)/include/ansi,$(CPPFLAGS))

CXXFLAGS    += $(WARNFLAGS)

# get a sorted list of config tests, starting with any shell scripts
# with the .sh suffix, followed by C++ (.cpp) programs
SRCS        := $(sort $(notdir $(wildcard $(SRCDIR)/*.sh)))
SRCS        += $(sort $(notdir $(wildcard $(SRCDIR)/*.cpp)))
OBJS        := $(SRCS:.cpp=.o)
TARGET      := $(SRCS:.cpp=)
PWD         := $(shell pwd)
DASH_H       = -H

CCNAME       = $(CXX)-$(CCVER)
LOGFILE      = config.log

# can't use LDOPTS when working with HP aCC, it's used by the linker
ifneq ($(CXX),aCC)
    LOPT     = LDOPTS
    LDFLAGS += $(LDOPTS)
else
    LOPT     = _LDOPTS
    LDFLAGS += $(_LDOPTS)
endif

# append $(LDLIBS) last, after $(LDOPTS), since the latter may depend
# on the former being after it on the link line
LDFLAGS     += -lm $(LDLIBS)

# CXXPRELINK - command for compilers that use template
#              instantiation models that require a prelink stage
ifneq ($(PRELINKFLAGS),)
CXXPRELINK   = $(CXX) $(CPPFLAGS) $(LDFLAGS) $(PRELINKFLAGS)
endif   # ($(PRELINKFLAGS),)

##############################################################################
#  TARGETS
##############################################################################

all: config.h


# recreate config.h and update its timestamp
config.h: $(SRCS)
	$(MAKE) config
	touch config.h

# (re)create config.h; do not change timestamp if the new file is the same
# make .o first to make sure the %.o: %.cpp rule gets invoked (make bug?)
# run a configure script as the last step (e.g., to remove unwanted files)
config: clean sane 
	@([ -f config.h ] && mv config.h config.h.~ ;                       \
          echo "// configured for $(CCNAME) on `uname -sr`" >config.h ;     \
          for file in $(TARGET) ; do                                        \
              src=$(SRCDIR)/$$file.cpp;                                     \
              desc=`head -n 1 2>/dev/null $$src | sed "s:// *::"`;          \
              [ "$$desc" != "" ] && printf "%-50.50s " "$$desc";            \
              grep "[^a-zA-Z0-9_]*main *(" $$src >/dev/null 2>&1 ;          \
              if [ $$? -eq 0 ] ; then                                       \
                  opts=`sed -n "s/^[^A-Z_a-z0-9]*LDOPTS *= *\(.*\)/\1/p"    \
                        $$src`;                                             \
                  targets="$$file.o $$file run RUN=$$file $(LOPT)=$$opts";  \
              elif [ `echo $$file | grep "\.lib"` ] ; then                  \
                  targets="$$file$(LIBSUFFIX)" ;                            \
              elif [ `echo $$file | grep "\.sh"` ] ; then                   \
                  if [ ! -f vars.sh ] ; then                                \
                      cat ../makefile.in                                    \
                      | sed -e "s/= *\([^ ][^ ]* .*\)/=\"\1\"/"             \
                            -e "s/^\([^ ]*\) *= *\(.*\)/\1=\2 ; export \1/" \
                            -e 's/$$(\([^)]*\))/${\1}/g' >vars.sh ;         \
                  fi ;                                                      \
                  $(SRCDIR)/$$file config.h $(LOGFILE) ;                    \
                  echo ;                                                    \
                  continue ;                                                \
              else                                                          \
                  targets="$$file.o run RUN=$$file.o" ;                     \
              fi;                                                           \
              $(MAKE) $$targets OUTPUT=config.h -k >/dev/null 2>&1 ;        \
              if [ "$$desc" != "" ] ; then                                  \
                  sym="_RWSTD_" ;                                           \
                  echo $$file | grep "^NO_" >/dev/null ;                    \
                  [ $$? -ne 0 ] && sym="$${sym}NO_" ;                       \
                  str=`sed -n "s/\(.*\)\(#\)define $$sym$$file$$/\1\2/p"    \
                       config.h`;                                           \
                  if [ "$$str" = "// #" ] ; then                            \
                       echo "ok";                                           \
                  elif [ "$$str" = "#" ] ; then                             \
                      echo "no ($$sym$$file)";                              \
                  else                                                      \
                      echo "--" ;                                           \
                  fi;                                                       \
              fi;                                                           \
          done;                                                             \
          diff config.h config.h.~ >/dev/null 2>&1 ;                        \
          if [ $$? -eq 0 ] ; then                                           \
              mv config.h.~ config.h ;                                      \
              echo "config.h unchanged";                                    \
          elif [ -f config.h~ ] ; then                                      \
              echo "previous config.h saved in config.h~";                  \
          fi;                                                               \
          [ -x ./configure ] && ./configure -f ./config.h -d $(TOPDIR) ;    \
          exit 0)

# run one or more (perhaps all) executables in $(RUN) or $(TARGET) if
# the former is not defined; if an executable doesn't exist and the .o
# doesn't contain main it is still considered success (this is needed
# when the run target is invoked from within the %.o: %.cpp pattern rule
# .o's are not run (obviously) and are just assumed to be the result of
# a successful compilation
run:
	@(output=$(OUTPUT) ;                                             \
          [ "$$output" = "" ] && output=/dev/tty ;                       \
          target=$(RUN) ;                                                \
          [ "$$target" = "" ] && target=$(TARGET) ;                      \
          for file in $$target ; do                                      \
              symbol="#define _RWSTD_" ;                                 \
              basename $$file | grep "^NO_" >/dev/null 2>&1 ;            \
              neg=$$? ;                                                  \
              [ $$neg -ne 0 ] && symbol="$${symbol}NO_" ;                \
              symbol="$${symbol}`echo $$file                             \
                      | sed -e 's:.*/::' -e 's/\.o//'`";                 \
              if [ `echo $$file | grep "\.o"` ] ; then                   \
                  test -r $$file ;                                       \
              elif [ ! -x $$file ] ; then                                \
                  nm -gp $$file.o 2>&1 | grep "T *main *$$" >/dev/null ; \
                  test -f $$file.o -a ! $$? -eq 0 ;                      \
              else                                                       \
                  echo "./$$file" >>$(LOGFILE) ;                         \
                  LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:. ;                  \
                  LIBPATH=$$LIBPATH:. ;                                  \
                  export LIBPATH LD_LIBRARY_PATH ;                       \
                  text=`./$$file` ;                                      \
              fi;                                                        \
              res=$$? ;                                                  \
                 [ $$res -eq 0 -a $$neg -ne 0 ]                          \
              || [ $$res -ne 0 -a $$neg -eq 0 ] && symbol="// $$symbol" ;\
              grep "$$symbol$$" config.h ;                               \
              if [ $$? -ne 0 ] ; then                                    \
                  echo "$$symbol" >>$$output;                            \
                  if [ "$$text" != "" ]; then                            \
                      printf "%s\n" "$$text" >>$$output;                 \
                  fi;                                                    \
              fi;                                                        \
          done;                                                          \
          exit 0)

# determine the type of architecture (LP32, ILP32, ILP64, LLP64, or LP64)
arch:
	@(output=$(OUTPUT);                                               \
          [ "$$output" = "" ] && output=/dev/tty ;                        \
	  for type in int long "long long" "void*" ; do                   \
              echo "int main () { return sizeof ($$type); }"              \
                   | tee a.cpp >>$(LOGFILE) ;                             \
              echo "$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c a.cpp "             \
                   "&& $(LD) a.o -a.out $(LDFLAGS)" >>$(LOGFILE) ;        \
              $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c a.cpp &&                  \
              $(LD) a.o $(LDFLAGS) -o a.out >>$(LOGFILE) 2>&1 ;           \
              if [ $$? -eq 0 ] ; then                                     \
                  size="$$size`./a.out ; echo $$?`" ;                     \
              else                                                        \
                  size="$${size}-";                                       \
              fi;                                                         \
          done;                                                           \
          case "$$size" in                                                \
              24?4 ) arch="LP32" ;;  44?4 ) arch="ILP32" ;;               \
              88?8 ) arch="ILP64" ;; 4488 ) arch="LLP64" ;;               \
              48?8 ) arch="LP64" ;;  *    ) arch="$$size" ;;              \
          esac ;                                                          \
          echo "int main () { int i = 1; return *(char*)&i; }"            \
               | tee a.cpp >>$(LOGFILE) ;                                 \
          echo "$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c a.cpp "                 \
               "&& $(LD) a.o -a.out $(LDFLAGS)" >>$(LOGFILE) ;            \
          $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c a.cpp &&                      \
          $(LD) a.o $(LDFLAGS) -o a.out >>$(LOGFILE) 2>&1 ;               \
          if [ $$? -eq 0 ] ; then                                         \
              endian=" little endian" ;                                   \
              ./a.out ;                                                   \
              [ $$? -eq 0 ] && endian=" big endian" ;                     \
          fi ;                                                            \
          echo "$$arch$$endian" >$$output ;                               \
         )

# check compiler, linker, and run environment's sanity, determine
# system (or compiler) architecture (word size, address size, etc.)
sane:
	@(echo; echo "configuring for $(CCNAME) on $(PLATFORM)"; echo;      \
          rm -f a.out ;                                                     \
          echo "int main () { return 0; }" | tee a.cpp >>$(LOGFILE) ;       \
          printf "%-50.50s " "checking if compiler is sane";                \
          echo "$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) a.cpp" >>$(LOGFILE) 2>&1; \
          $(CXX) -c $(CXXFLAGS) $(CPPFLAGS) a.cpp >>$(LOGFILE) 2>&1;        \
          if [ $$? -eq 0 ] ; then                                           \
              echo "ok";                                                    \
          else                                                              \
              echo "no"; echo;                                              \
              cat $(LOGFILE) ;                                              \
              exit 1;                                                       \
          fi;                                                               \
          printf "%-50.50s " "checking if linker is sane";                  \
          echo "$(LD) a.o $(LDFLAGS) -o a.out" >>$(LOGFILE) 2>&1;           \
          $(LD) a.o $(LDFLAGS) -o a.out >>$(LOGFILE) 2>&1 ;                 \
          if [ $$? -eq 0 ] ; then                                           \
              echo "ok";                                                    \
          else                                                              \
              echo "no"; echo;                                              \
              cat $(LOGFILE) ;                                              \
              exit 1;                                                       \
          fi;                                                               \
          printf "%-50.50s " "checking if run environment is sane";         \
          echo "./a.out" >>$(LOGFILE) 2>&1;                                 \
          ./a.out >>$(LOGFILE) 2>&1;                                        \
          if [ $$? -eq 0 ] ; then                                           \
              echo "ok";                                                    \
          else                                                              \
              echo "no"; echo;                                              \
              cat $(LOGFILE) ;                                              \
              exit 1;                                                       \
          fi;                                                               \
          printf "%-50.50s " "checking system architecture";                \
          `$(MAKE) arch OUTPUT=a.cpp>/dev/null 2>&1` ;                      \
          cat a.cpp ;                                                       \
          rm a.cpp )

clean:
	@rm -f a.out core *.o *.i *.ii *.ti vars.sh \
               *$(LIBSUFFIX) $(LOGFILE) $(TARGET)

realclean: clean
	rm -f *.d a.cpp config.h  

listtarget:
	@echo $(TARGET)


.PHONY: all clean config configure c_headers listtarget realclean run


##############################################################################
#  COMMON RULES
##############################################################################

#empty rule for .cc files so they won't be separately compiled
%: %.cc ;

# compile .cpp so that any macros used by the translation unit are configured
# checks config.h to make sure macro isn't yet defined before going recursive
# *.lib.cpp files a compiled (and linked) into libraries (shared or static)
# with LDOPTS reset to the empty string
%.o: %.cpp
	@(dep=`egrep "^ *# *if[n]*def  *_RWSTD_" $<                    \
               | sed -e "s/.*# *if[n]*def *\([^ ]*\) */\1/g"           \
                     -e "s/_RWSTD_NO_//g" -e "s/_RWSTD_//g"` ;         \
          for sym in $$dep ; do                                        \
              fname=$$sym ;                                            \
              [ ! -r $(SRCDIR)/$$fname.cpp ] && fname="NO_$$fname" ;   \
              grep "_RWSTD_NO_$$sym$$" config.h >/dev/null ;           \
              if [ $$? -ne 0 ] ; then                                  \
                  grep "[^a-zA-Z0-9_]*main *(" $(SRCDIR)/$$fname.cpp   \
                       >/dev/null 2>&1 ;                               \
                  if [ $$? -eq 0 ] ; then                              \
                      targets="$$fname.o $$fname run RUN=$$fname" ;    \
                  elif [ `echo $$fname | grep "\.lib"` ] ; then        \
                      targets="$$fname$(LIBSUFFIX) $(LOPT)=" ;         \
                  else                                                 \
                      targets="$$fname.o run RUN=$$fname.o" ;          \
                  fi;                                                  \
                  $(MAKE) $$targets OUTPUT=config.h -k ;               \
              fi;                                                      \
          done;                                                        \
          true)
	@echo "$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) $<" >>$(LOGFILE)
	$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) $< >>$(LOGFILE) 2>&1

# remove .o to prevent the run target from getting confused
%: %.o
	@echo "$(LD) $^ $(LDFLAGS) -o $@" >>$(LOGFILE) 2>&1
	$(LD) $^ $(LDFLAGS) -o $@ >>$(LOGFILE) 2>&1 ; rm $<

# build a library from any source file named *.lib.cpp
%.lib$(LIBSUFFIX): %.lib.cpp
ifeq ($(findstring shared,$(BUILDMODE)),shared)
  ifeq ($(findstring xlC,$(CXX)),xlC)
#       IBM xlC first "preprocesses" .o's with -qmkshrobj
#       and then uses `ar' to create a shared library...
	echo "$(CXX) $< $(CPPFLAGS) $(CXXFLAGS) $(PICFLAGS) -c -o $@.o" \
             >>$(LOGFILE)
	$(CXX) $< $(CPPFLAGS) $(CXXFLAGS) $(PICFLAGS) -c -o $@.o        \
             >>$(LOGFILE) 2>&1
	echo "$(CXXPRELINK) $@.o -o $@.lib.o" >>$(LOGFILE)
	$(CXXPRELINK) $@.o -o $@.lib.o  >>$(LOGFILE)
	echo "$(AR) $(ARFLAGS) $@ $@.lib.o" >>$(LOGFILE)
	$(AR) $(ARFLAGS) $@ $@.lib.o >>$(LOGFILE)
  else
	echo "$(CXX) $< $(CPPFLAGS) $(CXXFLAGS) $(PICFLAGS) -c -o $@.o" \
             >>$(LOGFILE)
	$(CXX) $< $(CPPFLAGS) $(CXXFLAGS) $(PICFLAGS) -c -o $@.o        \
             >>$(LOGFILE) 2>&1
	echo "$(LD) $@.o $(LDFLAGS) $(LDSOFLAGS) -o $@" >>$(LOGFILE)
	$(LD) $@.o $(LDFLAGS) $(LDSOFLAGS) -o $@ >>$(LOGFILE) 2>&1
  endif
else
	@echo "$(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -c -o $@.o" >>$(LOGFILE)
	$(CXX) $< -c -o $@.o $(CPPFLAGS) $(CXXFLAGS) >>$(LOGFILE) 2>&1
	@echo "$(AR) $(ARFLAGS) $@ $@.o" >>$(LOGFILE)
	$(AR) $(ARFLAGS) $@ $@.o >>$(LOGFILE)

endif
